﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using GameMap;
using GameLayer;
using GameCharacter;
using GameCharacter.AutoCharacter;
using GameInstrument;
using GameBase;
using MainGame.GameInstrument;
using System.Xml;
using MainGame.GameMap;
using Microsoft.Xna.Framework.Audio;

namespace MainGame
{
    public class Round
    {
        public static bool Initialized = false;
        public static Beetle Beetle { get; set; }
        public static Butcher Butcher { get; set; }
        public static GrayDino GrayDino { get; set; }
        public static Giant Giant { get; set; }
        public static Windmill Windmill { get; set; }
        public static TreeFactory TreeFactory { get; set; }
        public static DragonBall DragonBall { get; set; }
        public static SpriteFont font { get; set; }
        public static TiledLayer Background { get; set; }
        public static MyCharacter Hero { get; set; }
        public static Fire Fire { get; set; }
        public static SpriteFont DisplayBloodFont { get; set; }
        public static SpriteFont BigFont { get; set; }
        public int Index { get; set; }
        public bool Activity { get; set; }

        public static Arch Arch { get; set; }
        public List<AutoCharacter> Characters { get; set; }
        public List<Windmill> windmills { get; set; }
        public List<DragonBall> DragonBalls { get; set; }
        public MainGame Game { get; set; }
        public List<Weapon> Weapons;
        public MapDijkstra MapDijkstra { get; set; }
        public Dijkstra dijkstra { get; set; }
        public Map MapManager { get; set; }
        public Queue<NeedFindPath> NeedFindPath { get; set; }
        public bool IgnoreCollisionMap { get; set; }
        public int WidthViewport { get; set; }
        public int HeightViewPort { get; set; }
        public Vector2 Center { get; set; }
        public static SoundEffectInstance SoundBackground = SoundManager.Background.CreateInstance();
        public int Score { get; set; }
        int prevScore;

        public Round(MainGame game)
        {
            this.Game = game;
            this.Characters = new List<AutoCharacter>();
            this.windmills = new List<Windmill>();
            this.Weapons = new List<Weapon>();
            this.DragonBalls = new List<DragonBall>();
            this.NeedFindPath = new Queue<NeedFindPath>();
            Initialize();
            Hero.Blood = 2 * Character.MAX_BLOOD;
            prevScore = Hero.Score;
            if (this.Game.Index == 0)
                Hero.Level = 2;
        }

        public void Initialize()
        {
            XmlDocument doc = new XmlDocument();
            doc.Load(MainGame.CurrentRoundInfo.Map);
            LoadFromXML(doc.DocumentElement);
            WidthViewport = Game.GraphicsDevice.Viewport.Width;
            HeightViewPort = Game.GraphicsDevice.Viewport.Height;
            int xCenter = WidthViewport / 2;
            int yCenter = HeightViewPort / 2;
            Center = new Vector2(xCenter, yCenter);

            SoundBackground.Play();
        }

        public static void LoadContent(Game Game)
        {
            if (!Round.Initialized)
            {
                SoundBackground.IsLooped = true;
                SoundBackground.Volume = 1f;
                Round.Hero = new RedArcher(Game.Content);
                Round.Beetle = new Beetle(Game.Content);
                Round.Butcher = new Butcher(Game.Content);
                Round.GrayDino = new GrayDino(Game.Content);
                Round.Windmill = new Windmill(Game.Content);
                Round.Arch = new Arch(Game.Content);
                Round.TreeFactory = new TreeFactory(Game.Content);
                Round.DragonBall = new DragonBall(Game.Content);
                Round.Giant = new Giant(Game.Content);
                Round.DisplayBloodFont = Game.Content.Load<SpriteFont>("Font/displayBlood");
                Round.font = Game.Content.Load<SpriteFont>("Font/SpriteFont1");
                Round.BigFont = Game.Content.Load<SpriteFont>("Font/Big");
                Round.Background = new TiledLayer(203, 230, 128, 128, new Sprite(Game.Content.Load<Texture2D>("Images/Map/Default/background")));
            }
            Round.Initialized = true;
        }
        public static void UnloadContent()
        {
            if (Round.Initialized)
            {
                Round.Arch.Dispose();
                Round.Beetle.Dispose();
                Round.Butcher.Dispose();
                Round.GrayDino.Dispose();
                Round.Windmill.Dispose();
                Round.Arch.Dispose();
                foreach (var tree in Round.TreeFactory.GetAll())
                {
                    tree.Dispose();
                }
                Round.DragonBall.Dispose();

            }
            Round.Initialized = false;
        }
        public void Update(GameTime gameTime)
        {
            if (Hero.Score - prevScore > MainGame.CurrentRoundInfo.Score)
            {
                this.Game.Index++;
                this.Game.OpenPrevNextRound();
            }
            dijkstra = MapDijkstra.GetDijkstra(this.Game.GraphicsDevice.Viewport.Width, this.Game.GraphicsDevice.Viewport.Height);
            MouseState mouseState = Mouse.GetState();
            if (mouseState.LeftButton == ButtonState.Pressed)
            {
                LeftMousePressed(mouseState);
            }
            if (mouseState.RightButton == ButtonState.Pressed)
            {
                RightMousePressed(mouseState);
            }
            UpdateHero(gameTime, ref mouseState);
            Vector2 d = Center - Hero.Position;
            if (MapManager.Position.X - d.X < 0)
            {
                d.X = 0;
            }
            if (MapManager.Position.Y - d.Y < 0)
                d.Y = 0;
            Hero.Position += d;
            MapManager.Position -= d;

            for (int i = 0; i < DragonBalls.Count; i++)
            {
                var ball = DragonBalls[i];
                ball.Update(gameTime, null);
                ball.Position += d;
                if (ball.Durability <= 0 && ball.CollisionWith(Hero, false))
                {
                    DragonBalls.RemoveAt(i);
                    i--;
                    Hero.Power++;
                    Hero.Blood = 2 * Character.MAX_BLOOD;
                }
                var ws = ball.ReleasePowerAttack();
                if (ws != null)
                    this.Weapons.AddRange(ws);
            }

            for (int i = 0; i < Characters.Count; i++)
            {
                var c = Characters[i];
                if (c.Visible == false)
                {
                    Characters.RemoveAt(i);
                    i--;
                    continue;
                }
                c.Position += d;
                UpdateAutoCharacter(gameTime, mouseState, c);
            }

            if (NeedFindPath.Count > 0)
            {
                NeedFindPath need = NeedFindPath.Dequeue();
                //Gỉa sử row,column còn trong viewport hiện thời
                Character c = need.Character;
                c.Targets = dijkstra.FindPath(need.Start, need.End);
                if (c.Targets == null)
                    c.Attack();

            }
            int len = this.Weapons.Count;
            for (int i = 0; i < len; i++)
            {
                var w = this.Weapons[i];
                if (w.Visible == false)
                {
                    this.Weapons.Remove(w);
                    i--;
                    len--;
                    continue;
                }
                w.Update(gameTime, null);
                w.Position -= d;
                if (w.Own == null || w.Own is AutoCharacter)
                {
                    if (w.CollisionWith(Hero, false))
                    {
                        BeAttacked(w, Hero);
                    }
                }
                if (w.Own == null || w.Own is MyCharacter)
                {
                    if (w.Own != null)
                    {
                        for (int k = 0; k < this.DragonBalls.Count; k++)
                        {
                            var ball = this.DragonBalls[k];
                            if (ball.CollisionWith(w, false))
                            {
                                if (!w.Exploding)
                                {
                                    w.Explosive();
                                    ball.Durability--;
                                    break;
                                }
                            }
                        }
                    }
                    for (int j = 0; j < Characters.Count; j++)
                    {
                        var c = Characters[j];
                        if (w.CollisionWith(c, false))
                        {
                            if (!w.Exploding)
                            {
                                w.Explosive();
                                c.BeAttacked(w.Power);
                                Hero.Score += w.Power;
                            }
                            else
                            {
                                c.Blood--;
                                Hero.Score++;
                            }
                        }
                    }
                }
                if (!w.Exploding)
                {
                    Vector2 size = w.Size;
                    Vector2 pos = w.Position;
                    Rectangle rect = new Rectangle((int)pos.X, (int)pos.Y, (int)size.X, (int)size.Y);
                    if (!IsInViewport(rect))
                    {
                        this.Weapons.Remove(w);
                        i--;
                        len--;
                        continue;
                    }
                }
            };
        }

        private static void BeAttacked(Weapon w, Character c)
        {
            if (!w.Exploding)
            {
                w.Explosive();
                c.BeAttacked(w.Power);
            }
            else
            {
                c.Blood--;
            }
        }

        private void UpdateHero(GameTime gameTime, ref MouseState mouseState)
        {
            Hero.Update(gameTime, mouseState);
            if (!Hero.IsDying())
            {
                if (Hero.Blood < 1)
                {

                    Hero.BeginDie();

                }
                else
                {

                }
            }
            else
            {
                if (Hero.Died())
                {
                    if (Hero.Live > 0)
                    {
                        this.Game.OpenContinue();
                    }
                    else
                    {
                        GameOver();
                    }
                }
                return;
            }
            var prevPosHero = Hero.Position;
            if (Hero.Targets != null && Hero.Targets.Count > 0)
            {
                GoToNextTarget(Hero);
                Hero.Position += MapDijkstra.Position;
                bool canGo = IgnoreCollisionMap || !Hero.CollisionWith(MapDijkstra, false);
                Hero.Position -= MapDijkstra.Position;
                if (!canGo && Hero.Targets.Count > 0)
                {
                    Hero.Position = prevPosHero;
                    Point start = MapDijkstra.Position2RoundColumn(Hero.Position + Hero.Size / 2 + MapDijkstra.Position);
                    Point end = Hero.Targets.Last();
                    Hero.Targets = dijkstra.FindPath(start, end);
                    IgnoreCollisionMap = true;
                }
            }
            ReleasePowerAttack(Hero);
        }

        private void GameOver()
        {
            SoundBackground.Stop();
            this.Game.OpenGameOver();
        }

        private void UpdateAutoCharacter(GameTime gameTime, MouseState mouseState, AutoCharacter c)
        {
            c.Update(gameTime, mouseState);

            if (!c.IsDying() && c.Blood <= 0)
            {
                c.BeginDie();
                return;
            }
            if (c.Targets != null && c.Targets.Count > 0)
            {
                GoToNextTarget(c);
            }
            else
            {
                var pos = c.Position;
                var size = c.Size;
                AutoCharacter autoCharacter = (AutoCharacter)c;
                if (IsInViewport(new Rectangle((int)pos.X, (int)pos.Y, (int)size.X, (int)size.Y)))
                {
                    if (NeedFindPath.FirstOrDefault(x => x.Character == c) == null)
                    {
                        Point start = MapDijkstra.Position2RoundColumn(c.Position + c.Size / 2 + MapDijkstra.Position);
                        Point end = MapDijkstra.Position2RoundColumn(Hero.Position + Hero.Size / 2 + MapDijkstra.Position);
                        NeedFindPath need = new NeedFindPath
                    {
                        Character = c,
                        Start = start,
                        End = end
                    };
                        NeedFindPath.Enqueue(need);
                    }
                }
                else
                {
                    bool goToBack = false;
                    if (autoCharacter.Home.HasValue)
                    {
                        var homepos = autoCharacter.Home.Value - MapDijkstra.Position;
                        float dictance = Vector2.Distance(homepos, autoCharacter.Position);
                        goToBack = dictance > 1000;
                        if (goToBack)
                        {
                            autoCharacter.Position = homepos;
                        }
                    }

                    if (!goToBack && Vector2.Distance(c.Position, Hero.Position) < 1000)
                    {
                        c.GoToTarget(Hero.Position);
                        c.Position += c.Velocity;
                        if (c.CollisionWith(MapDijkstra, false))
                        {
                            c.Position -= c.Velocity;
                        }
                    }
                }
            }

            if (Vector2.Distance(c.Position, Hero.Position) < 200)
            {
                c.Targets = null;
                c.GoToTarget(Hero.Position);
                c.Attack();
                c.AttackTarget = Hero.Position;
            }
            ReleasePowerAttack(c);
        }

        private void ReleasePowerAttack(Character c)
        {
            var ws = c.ReleasePowerAttack();
            if (ws != null)
            {
                this.Weapons.AddRange(ws);
            }
        }

        private void GoToNextTarget(Character c)
        {
            Point first = c.Targets.First();
            Vector2 t = MapDijkstra.Global_RoundColumn2Position(first) - MapDijkstra.Position;
            c.GoToTarget(t);
            if (Vector2.Distance(t, c.Position) < 10)
            {
                c.Targets.RemoveAt(0);
            }
            else
            {
                c.Current.NextFrame();
                c.Position += c.Velocity;
            }
        }

        private void RightMousePressed(MouseState mouseState)
        {
            Point start = MapDijkstra.Position2RoundColumn(Hero.Position + Hero.Size / 2 + MapDijkstra.Position);
            Point end = MapDijkstra.Position2RoundColumn(MapDijkstra.Position + new Vector2(mouseState.X, mouseState.Y));
            Hero.Targets = new List<Point> { end }; //dijkstra.FindPath(start, end);
            IgnoreCollisionMap = false;
        }

        private void LeftMousePressed(MouseState mouseState)
        {
            Vector2 target = new Vector2(mouseState.X, mouseState.Y);
            Hero.Targets = null;
            Hero.GoToTarget(target);
            Hero.AttackTarget = target;
            Hero.Attack();
        }

        public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
        {
            Viewport viewport = this.Game.GraphicsDevice.Viewport;
            MapManager.Draw(gameTime, spriteBatch, viewport);
            DrawCharacter(gameTime, spriteBatch, viewport, Hero);
            Characters.ForEach(c =>
            {
                DrawCharacter(gameTime, spriteBatch, viewport, c);
            });
            if (Weapons.Count != 0)
            {
                int x = Weapons.Count;
                Weapons.ForEach(w =>
                {
                    w.Draw(gameTime, spriteBatch, null);
                });
            }
            DragonBalls.ForEach(ball => { ball.Draw(gameTime, spriteBatch, null); });
            spriteBatch.DrawString(Round.font, "Level  : " + Hero.Level, new Vector2(10, 10), Color.Black);
            spriteBatch.DrawString(Round.font, "Power  : " + Hero.Power, new Vector2(10, 40), Color.Black);
            spriteBatch.DrawString(Round.font, "Live : " + Hero.Live, new Vector2(Game.GraphicsDevice.Viewport.Width - 200, 10), Color.Black);
            spriteBatch.DrawString(Round.font, "Score : " + Hero.Score, new Vector2(Game.GraphicsDevice.Viewport.Width - 200, 40), Color.Black);

        }

        private static void DrawCharacter(GameTime gameTime, SpriteBatch spriteBatch, Viewport viewport, Character c)
        {
            c.Draw(gameTime, spriteBatch, viewport);
            spriteBatch.DrawString(Round.DisplayBloodFont, c.Blood.ToString(), c.Position + new Vector2(0, -20), Color.Red);
        }

        public bool IsInViewport(Rectangle area)
        {
            return this.Game.GraphicsDevice.Viewport.Bounds.Intersects(area);
        }

        public void LoadFromXML(XmlElement xmlRound)
        {
            {
                XmlElement xmlHero = xmlRound.SelectSingleNode("Hero") as XmlElement;
                int x = int.Parse(xmlHero.GetAttribute("x"));
                int y = int.Parse(xmlHero.GetAttribute("y"));
                Hero.Position = new Vector2(x, y);
            }
            XmlNodeList xmlEnemies = xmlRound.SelectNodes("Enemies/Enemy");
            foreach (XmlElement xml in xmlEnemies)
            {
                AutoCharacter c = null;
                int type = int.Parse(xml.GetAttribute("type"));
                switch (type)
                {
                    case 0:
                        c = Round.Beetle.Clone() as AutoCharacter;
                        break;
                    case 1:
                        c = Round.Butcher.Clone() as AutoCharacter;
                        break;
                    case 2:
                        c = Round.GrayDino.Clone() as AutoCharacter;
                        break;
                    case 3:
                        c = Round.Giant.Clone() as AutoCharacter;
                        break;
                    default:
                        throw new Exception("Type khong dung");
                }
                int x = int.Parse(xml.GetAttribute("x"));
                int y = int.Parse(xml.GetAttribute("y"));
                int xhome = 0;
                int yhome = 0;
                if (int.TryParse(xml.GetAttribute("x-home"), out xhome) | /*hop bit*/
                    int.TryParse(xml.GetAttribute("y-home"), out yhome))
                {
                    c.Home = new Vector2(xhome, yhome);
                }
                c.Position = new Vector2(x, y);

                if (xml.HasAttribute("level"))
                {
                    c.Level = int.Parse(xml.GetAttribute("level"));
                }
                if (xml.HasAttribute("power"))
                {
                    c.Power = int.Parse(xml.GetAttribute("power"));
                }
                this.Characters.Add(c);
            }
            XmlNodeList xmlDragonBalls = xmlRound.SelectNodes("DragonBalls/DragonBall");
            foreach (XmlElement xml in xmlDragonBalls)
            {
                var ball = new DragonBall(Game.Content, xml);
                this.DragonBalls.Add(ball);
            }
            XmlElement mainTiledLayer = xmlRound.SelectSingleNode("TiledLayers/TiledLayer[@name='main'][@type='MainGame.GameMap.MapDijkstra']") as XmlElement;
            this.MapDijkstra = (MapDijkstra)TiledLayer.CreateTiledLayer(mainTiledLayer, TreeFactory.GetAll());
            MapManager = new Map(Game.Content, new List<Layer> { Round.Background, this.MapDijkstra });
        }
    }
}
